layui.define(["jquery", "form"], function (exports) {
    "use strict";

    // 元素
    let mainViewElement = document.querySelector('main-view');

    let $ = layui.$,
        form = layui.form,
        config = {
            css: [],
            page: "/page",
        },
        // 模板数据
        template = "",
        // 监听数据
        proxyObj = {},
        bind = {}


    //监听哈希
    window.addEventListener('hashchange', () => {
        mainViewElement.removes();
        jump();
    });

    // 渲染
    let app = {
        /**
         * 渲染
         * @param {object} options 
         */
        render: (options) => {
            config.css = options.css || config.css
            config.page = options.page || config.page
            layui.each(options.css, (k, v) => {
                layui.link(v)
            })
            if ($("main-view").length) {
                jump()
            }
        },
        /**
         * 数据监听
         * @param {object} object 
         */
        proxy: (object) => {
            if (!Object.keys(object).length) {
                return proxyObj
            }
            proxyObj = new Proxy(object, {
                get(target, property, receiver) {
                    try {
                        return new Proxy(target[property], {
                            set(childTarget, childProperty, value) {
                                const result = Reflect.set(childTarget, childProperty, value);
                                proxyObj = target
                                templateUpload()
                                return result;
                            },
                        });
                    } catch (e) {
                        return Reflect.get(target, property, receiver);
                    }
                },
                set(target, property, value) {
                    const result = Reflect.set(target, property, value);
                    proxyObj = target
                    templateUpload()
                    return result;
                },
            })
            templateUpload()
            return proxyObj;
        },
        // 挂载
        onMount: () => { },
        // 渲染前
        beforeUpdate: () => { },
        // 渲染后
        afterUpdate: () => { },
        // 卸载
        onDestroy: () => { },
    }

    // 跳转
    let jump = (arr = layui.url().hash.path) => {
        if (arr.length >= 1 && arr[0] != "") {
            var htmlView = arr.join("/")
        } else {
            var htmlView = "home";
        }
        onDestroy(app.onDestroy)
        let url = config.page + "/" + htmlView + "/" + htmlView;
        mainViewElement.baseCss()
        mainViewElement.addJsAndCss(url)
        $.get(url + ".html", (res) => {
            onMount(app.onMount)
            template = res
            templateUpload(res)
        })
    }

    // 模板渲染
    let templateUpload = (str = template) => {
        beforeUpdate(app.beforeUpdate)
        let div = mainViewElement.shadowRoot.querySelector("div")
        div.innerHTML = jmTpl(str, proxyObj)
        afterUpdate(app.afterUpdate)
    }

    class WebComponents extends HTMLElement {
        constructor() {
            super();
            // 创建 Shadow DOM
            const shadowRoot = this.attachShadow({ mode: 'open' });
        }

        // 初始化css
        baseCss() {
            if (config.css.length > 0) {
                config.css.forEach((v) => {
                    let link = document.createElement('link')
                    link.href = v
                    link.rel = 'stylesheet';
                    this.shadowRoot.appendChild(link)
                })
            }
        }

        // 添加js和css
        addJsAndCss(url) {
            let link = document.createElement('link')
            link.href = url + ".css"
            link.rel = 'stylesheet';
            let script = document.createElement('script')
            script.src = url + ".js"
            let div = document.createElement('div')
            div.innerHTML = "";
            this.shadowRoot.appendChild(script)
            this.shadowRoot.appendChild(link)
            this.shadowRoot.appendChild(div)
        }

        // 清空
        removes() {
            // 移除之前的内容
            while (this.shadowRoot.firstChild) {
                this.shadowRoot.removeChild(this.shadowRoot.firstChild);
            }
        }
    }

    /**
     * 挂载
     * @param {callback} callback 
     */
    let onMount = (callback) => {
        callback()
    }
    /**
     * 渲染前
     * @param {callback} callback 
     */
    let beforeUpdate = (callback) => {
        callback()
    }

    /**
     * 渲染后
     * @param {callback} callback 
     */
    let afterUpdate = (callback) => {
        callback()
        form.render();
    }

    /**
     * 卸载
     * @param {callback} callback 
     */
    let onDestroy = (callback) => {
        callback()
        app.onMount = () => { }
        app.beforeUpdate = () => { }
        app.afterUpdate = () => { }
        app.onDestroy = () => { }
        bind = {}
    }

    // 判断是否有main-view
    if ($("main-view").length) {
        if (!customElements.get('main-view')) {
            // 注册自定义元素
            customElements.define('main-view', WebComponents);
            bind = $(document.querySelector('main-view').shadowRoot)
        }
    }

    // 工具
    var tool = {
        /**
         * 筛选语句
         * @param {string} str 
         * @returns
         */
        filtrate: function (str) {
            return ["{#if", "{:else", "{/if", "{/each", "{#each"].some(function (prefix) {
                return str.startsWith(prefix);
            })
        },
        /**
         * html转义
         * @param {string} html 
         * @returns 
         */
        escape: function (html) {
            var div = document.createElement('div');
            div.appendChild(document.createTextNode(html));
            return div.innerHTML;
        },
        /**
         * data中的变量替换
         * @param {string} str 
         * @param {object} data 
         * @returns 
         */
        forhop: function (str, data) {
            var arr = str.split(" ")
            return arr
        }
    };

    /**
     * 模板渲染
     * @param {string} template 模板字符串
     * @param {object} data 数据
     * @returns string
     */
    function jmTpl(template, data) {
        if (Object.keys(data).length === 0) {
            return template;
        } else {
            // 模板必须为 string 类型
            if (typeof template !== 'string') return template;
            template = template
                .replace(/\{\s*\n*\s*/g, "{")
                .replace(/\s*\n*\s*\}/g, "}")
                .replace(/\s*\n*\s*\,/g, ",")
                .replace(/\,\s*\n*\s*/g, ",");

            var templateArr = template.split("\n");
            var strTem = "var view = '';";
            for (var i = 0; i < templateArr.length; i++) {
                templateArr[i] = templateArr[i].trim()
                if (tool.filtrate(templateArr[i])) {
                    strTem += templateArr[i].replace(/{#if\s([\s\S]*?)}/g, function (m, p) {
                        return `if(${p}){`
                    }).replace(/{:else\sif\s([\s\S]*?)}/g, function (m, p) {
                        return `}else if(${p}){`
                    }).replace(/{\/if}/g, `}`).replace(/{:else}/g, `}else{`)
                        .replace(/{#each\s([\s\S]*?)}/g, function (m, p) {
                            var eachArr = tool.forhop(p, data)
                            return `${eachArr[0]}.forEach(function(${eachArr[2]}){`
                        }).replace(/{\/each}/g, "});")
                } else if (templateArr[i] != "") {
                    if (/\{\@html\s[^}]+\}/g.test(templateArr[i])) {
                        templateArr[i] = templateArr[i].replace(/(\{\@html\s([^}]+)\})/g, function (m, p1, p2) {
                            return `\${${p2}}`
                        })
                    } else {
                        templateArr[i] = templateArr[i].replace(/(\{([^}]+)\})/g, function (m, p1, p2) {
                            return `\${tool(${p2})}`
                        })
                    }
                    strTem += `view += \`${templateArr[i]}\`;\n`
                }
            }
            try {
                var fn = new Function(`d,tool`, `${strTem}return view;`);
                return fn(data, tool.escape);
            } catch (e) {
                console.error(`${e}\n${strTem}`)
                return strTem;
            }
        }

    }

    exports('app', { app, bind }); // 输出模块
});  